Skip to content

feat: add find command for catalog search#174

Open
nadavs123 wants to merge 29 commits intomicrosoft:mainfrom
nadavs123:feature/catalog-search-find-command
Open

feat: add find command for catalog search#174
nadavs123 wants to merge 29 commits intomicrosoft:mainfrom
nadavs123:feature/catalog-search-find-command

Conversation

@nadavs123
Copy link

📥 Pull Request

Closes #172

Summary

Adds a new find command that searches for Fabric items across all accessible workspaces using the Catalog Search API (POST /v1/catalog/search).

Usage

# Basic search
fab find "sales report"

# Filter by item type(s)
fab find "data" --type Lakehouse
fab find "monthly" --type Report Warehouse

# Limit results
fab find "dashboard" --max-items 10

# Detailed view (shows IDs for scripting)
fab find "sales" -l

# Pagination
fab find --next-token "eyJTa2lwIjoy..."

Flags

Flag Description
--type Filter by item type(s), space-separated
--max-items Maximum results per page (1-1000)
-l/--long Show detailed output with IDs
--next-token Continuation token for next page

Implementation details

  • Follows existing CLI patterns: @fab_command decorator, print_output_format(), FabricCLIError
  • Tab-completion for --type via argcomplete
  • --max-items validation (1-1000)
  • Structured error handling for unsupported/unknown types, missing query, API failures
  • Description column hidden in compact mode when no items have descriptions
  • Empty fields hidden in long (-l) mode
  • Pagination via --next-token (continuation token approach)

Files added

  • src/fabric_cli/client/fab_api_catalog.py — API client
  • src/fabric_cli/commands/find/__init__.py — Package init
  • src/fabric_cli/commands/find/fab_find.py — Command logic
  • src/fabric_cli/parsers/fab_find_parser.py — Argument parser
  • tests/test_commands/find/__init__.py — Test package init
  • tests/test_commands/find/test_find.py — 22 unit tests
  • .changes/unreleased/added-20260209-171617.yaml — Changie entry

Files modified

  • src/fabric_cli/core/fab_parser_setup.py — Register find parser

Notes

  • Dashboard is the only unsupported item type
  • Dataflow Gen1/Gen2 are currently not searchable; only Dataflow Gen2 CI/CD is returned (as type Dataflow)
  • Scorecards are returned as type Report
  • Requires Catalog.Read.All scope (not yet added to CLI app registration)

Nadav Schachter added 21 commits February 9, 2026 17:51
Features:
- Search across all workspaces by displayName, workspaceName, or description
- Filter by item type with --type flag
- Limit results with --limit flag
- Detailed output with --detailed flag (includes id, workspaceId)
- Custom endpoint support with --endpoint flag or FAB_CATALOG_ENDPOINT env var

Output columns (default): name, type, workspace, description
Output columns (detailed): + workspaceId, id

Required scope: Catalog.Read.All
Unsupported types: Dashboard, Dataflow, Scorecard

Includes unit tests (12 tests passing)
…dling

Changes based on issue microsoft#172 feedback:
- Changed --type from comma-separated to nargs='+' (space-separated)
- Removed --endpoint flag (use internal mechanism instead)
- Added FabricCLIError for invalid/unsupported item types
- Added error handling for API failures
- Updated tests to match new patterns (15 tests passing)
- Added complete_item_types() completer for searchable types
- Tab completion excludes unsupported types (Dashboard, Dataflow, Scorecard)
- Restored unsupported type validation with clear error message
- Updated ALL_ITEM_TYPES list from official API spec
- Added SEARCHABLE_ITEM_TYPES for valid filter types
- 20 tests passing
- Keep tab-completion for --type flag
- Custom FabricCLIError for unsupported types (Dashboard, Dataflow, Scorecard)
- Custom FabricCLIError for unknown types
- Cleaner error messages vs argparse choices listing all 40+ types
- 22 tests passing
- Changed from data= to json= for request payload
- Added raw_response=True to avoid auto-pagination hanging
- Added fallback from displayName to name (API bug workaround)
- Updated tests to use dict instead of JSON string
- Successfully tested against dailyapi.fabric.microsoft.com
@nadavs123 nadavs123 requested a review from a team as a code owner February 15, 2026 09:54
Nadav Schachter and others added 7 commits February 24, 2026 16:25
…ination and -P params

- Interactive mode: pages 50 items at a time with 'Press any key to continue...'
- Command-line mode: fetches up to 1000 items in a single request
- Replace --type with -P type=Report,Lakehouse (key=value pattern)
- Remove --max-items and --next-token flags

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Catalog Search API returns an empty string continuationToken
on the last page instead of null/omitting it. This caused the
interactive pagination loop to send an empty token on the next
request, which the API treats as a fresh empty search — returning
unrelated results from the entire tenant.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Long descriptions caused the table separator line to wrap, appearing
as a double separator. Descriptions are now truncated with ellipsis
to keep the table within the terminal width. Full descriptions are
still available via -l/--long mode.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Command-line mode now fetches all pages instead of stopping at one
page of 1000. Uses the same continuation token pattern as interactive
mode with 'or None' guard against empty string tokens.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use contractions and active voice for friendlier tone
- Suggest close matches for unknown types instead of dumping all 43
- Remove redundant type list from unsupported type error

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>


# All Fabric item types (from API spec, alphabetically sorted)
ALL_ITEM_TYPES = [
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we have those types from an API and save it on init?
If not, please move it to separate file, maybe fab_constant or create type_supported.yaml - same as we have command_supported.

]

# Types that ARE searchable (for validation)
SEARCHABLE_ITEM_TYPES = [t for t in ALL_ITEM_TYPES if t not in UNSUPPORTED_ITEM_TYPES]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not having supported and unsupported type list?

if args.query:
args.query = utils.process_nargs(args.query)

is_interactive = getattr(args, "fab_mode", None) == fab_constant.FAB_MODE_INTERACTIVE
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current fab_mode check does not always indicate that the CLI is running in interactive mode. When the user runs fab and presses Enter, the CLI enters interactive mode even if the configuration mode is set to command_line. You may want to pass this explicitly to the command as a parameter.

from fabric_cli.client.fab_api_types import ApiResponse


def catalog_search(args: Namespace, payload: dict) -> ApiResponse:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit - since we are in catalog_api consider rename to search(args: Namespace, payload: dict)


def _raise_on_error(response) -> None:
"""Raise FabricCLIError if the API response indicates failure."""
if response.status_code != 200:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we getting to this point?
since do_request() handle cases where status is not 200 and throw an error that is catch by handle_exceptions decorator

required=False,
metavar="",
nargs="*",
help="Parameters in key=value or key!=value format. Use brackets for multiple values: type=[Lakehouse,Notebook]. Use != to exclude: type!=Dashboard",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you parse it with get_dict_from_params, I think you will need to use quats: type=["Lakehouse","Notebook"]

"-l",
"--long",
action="store_true",
help="Show detailed output. Optional",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

required=False ?

issue-172.md Outdated
@@ -0,0 +1,312 @@
### Use Case / Problem
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this file?
should be removed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry it's just the draft of the issue itself. I'll remove it.

@@ -0,0 +1,2 @@
# Copyright (c) Microsoft Corporation.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove and place the test_find directly under test_command

assert payload["filter"] == "(Type ne 'Dashboard' and Type ne 'Datamart')"


class TestParseTypeParam:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should create e2e tests that call find command and record the tests and validate all different scenarios instead of those unit tests.

@aviatco
Copy link
Collaborator

aviatco commented Mar 15, 2026

Please update the description:

fab find "monthly" --type Report Warehouse - should be list

fab find "dashboard" --max-items 10 - flag doesnt exist

fab find --next-token "eyJTa2lwIjoy..." - flag doesnt exist

@@ -0,0 +1,2 @@
# Copyright (c) Microsoft Corporation.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add documentation for this new command - under docs folder.
here you can find the guidelines how to rebuild it locally

This is just a draft of the issue I opened
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Add find command for catalog search

2 participants